package com.citywithincity.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.damai.error.ErrorUtil;

public class ClassUtil {

	
	

	/**
	 * 
	 * 获取一个类的所有泛型信息
	 * @param clazz
	 * @param index
	 * @return
	 */
	public static Type[] getTypeParams(final Class<?> clazz) {

		  // TODO 这个实现会导致泛型丢失,只能取得申明类型
        if (clazz == null || "java.lang.Object".equals(clazz.getName()))
            return null;
        // 看看父类
        Type superclass = clazz.getGenericSuperclass();
        if (null != superclass && superclass instanceof ParameterizedType)
            return ((ParameterizedType) superclass).getActualTypeArguments();

        // 看看接口
        Type[] interfaces = clazz.getGenericInterfaces();
        for (Type inf : interfaces) {
            if (inf instanceof ParameterizedType) {
                return ((ParameterizedType) inf).getActualTypeArguments();
            }
        }
        return getTypeParams(clazz.getSuperclass());
	}
	
	public static Class<?> getGenricType(final Class<?> clazz){
		return (Class<?>) getTypeParams(clazz)[0];
	}
	/**
	 * 获取<T>
	 * 
	 * @param clazz
	 * @param index
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Class<Object> getSuperClassGenricType(final Class<?> clazz, final int index) {

		// 返回表示此 Class 所表示的实体（类、接口、基本类型或 void）的直接超类的 Type。
		Type genType = clazz.getGenericSuperclass();

		if (!(genType instanceof ParameterizedType)) {
			return Object.class;
		}
		// 返回表示此类型实际类型参数的 Type 对象的数组。
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		if (index >= params.length || index < 0) {
			return Object.class;
		}
		if (!(params[index] instanceof Class)) {
			return Object.class;
		}

		return (Class<Object>) params[index];
	}

	/**
	 * dest 是否 是 src的包装类
	 * 
	 * @param src
	 * @param dest
	 * @return
	 */
	public static boolean isWapClass(Class<?> src, Class<?> dest) {
		try {
			return ((Class<?>) dest.getField("TYPE").get(null)) == src;
		} catch (Exception e) {
			return false;
		}
	}

	public static List<Class<?>> getInsterfaces(Class<?> clazz) {
		List<Class<?>> list = new ArrayList<Class<?>>();
		boolean working = true;
		do {
			Class<?>[] interfaces = clazz.getInterfaces();
			if (interfaces != null) {
				for (Class<?> class1 : interfaces) {
					list.add(class1);
				}
			}
			clazz = clazz.getSuperclass();
			working = !clazz.equals(Object.class);
		} while (working);
		return list;
	}

	@SuppressWarnings("unchecked")
	public static <T> T mapToObject(Map<String, Object> map, Object obj) {
		try {
			Field[] fields = obj.getClass().getFields();
			for (Field field : fields) {
				if ((field.getModifiers() & Modifier.STATIC) > 0) {
					continue;
				}

				String name = field.getName();
				if (map.containsKey(name)) {
					field.set(obj, map.get(name));
				}
			}
			return (T) obj;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 
	 * @param constructor
	 * @param list
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static <T> T newInstance(Constructor<?> constructor, Object[] list) throws Exception {
		Method cMethod = constructor.getClass().getDeclaredMethod("newInstance",
				Array.newInstance(Object.class, 0).getClass());
		Object objs = Array.newInstance(Object.class, list.length);
		int index = 0;
		for (Object object : list) {
			Array.set(objs, index++, object);
		}
		return (T) cMethod.invoke(constructor, objs);
	}

	@SuppressWarnings("unchecked")
	public static <T> T newInstance(Constructor<?> constructor, List<Object> list) throws Exception {
		Method cMethod = constructor.getClass().getDeclaredMethod("newInstance",
				Array.newInstance(Object.class, 0).getClass());
		Object objs = Array.newInstance(Object.class, list.size());
		int index = 0;
		for (Object object : list) {
			Array.set(objs, index++, object);
		}
		return (T) cMethod.invoke(constructor, objs);
	}

	public static <T> T newInstance(Class<T> clazz, Object[] list) throws Exception {
		Class<?>[] parameterTypes = new Class<?>[list.length];
		int index = 0;
		for (Object obj : list) {
			parameterTypes[index++] = obj.getClass();
		}
		Constructor<?> constructor = clazz.getConstructor(parameterTypes);
		return newInstance(constructor, list);

	}
	
	
	public static <T> T newInstanceSafe(Class<T> clazz){
		try {
			return clazz.newInstance();
		} catch (Exception e) {
			throw ErrorUtil.wrapThrow(e);
		}
	}

	// 获取所有
	private static void getFields(List<Field> result, Class<?> clazz) {
		if(clazz==null){
			return;
		}
		Field[] list = clazz.getDeclaredFields();
		for (Field field : list) {
			if ((field.getModifiers() & java.lang.reflect.Modifier.STATIC) > 0) {
				continue;
			}
			if (field.getName().equals("class")) {
				continue;
			}
			result.add(field);
		}
	}

	/**
	 * 
	 * @param clazz
	 * @return
	 */
	public static Field getField(Class<?> clazz, String name) {
		try {
			Field result = null;
			try {
				result = clazz.getField(name);
				return result;
			} catch (NoSuchFieldException e) {

			}
			do {
				result = clazz.getDeclaredField(name);
				if (result != null) {
					return result;
				}
				clazz = clazz.getSuperclass();
				if (clazz == Object.class) {
					throw new RuntimeException("Cannot find field " + name + " in " + clazz.getName());
				}
			} while (true);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 获取public field
	 * 
	 * @param clazz
	 * @return
	 */
	public static Field[] getFields(Class<?> clazz) {
		
		Class<?> tmp = clazz;
		if(tmp==Object.class || tmp==null){
			return new Field[0];
		}
		List<Field> result = new ArrayList<Field>();
		do {
			if(tmp==null){
				break;
			}
			getFields(result, tmp);
			tmp = tmp.getSuperclass();
			if (tmp == Object.class) {
				break;
			}
		} while (true);
		Field[] list = new Field[result.size()];
		result.toArray(list);
		return list;
	}

	/**
	 * 设置静态变量的值
	 * 
	 * @param clazz
	 * @param fieldName
	 * @param value
	 * @throws Exception
	 */
	public static void setStaticFieldValue(Class<?> clazz, String fieldName, Object value) throws Exception {
		Field field = clazz.getField(fieldName);
		field.setAccessible(true);
		field.set(null, value);
	}

	public static Object getStaticFieldValue(Class<?> clazz, String fieldName) throws Exception {
		return clazz.getField(fieldName).get(null);
	}

	public static String getFieldName(String name) {
		String str = name.substring(3);
		return StringUtils.firstCharToLowerCase(str);
	}

	/*
	 * 取得某一类所在包的所有类名 不含迭代
	 */
	public static String[] getPackageAllClassName(String classLocation, String packageName) {
		// 将packageName分解
		String[] packagePathSplit = packageName.split("[.]");
		String realClassLocation = classLocation;
		int packageLength = packagePathSplit.length;
		for (int i = 0; i < packageLength; i++) {
			realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
		}
		File packeageDir = new File(realClassLocation);
		if (packeageDir.isDirectory()) {
			String[] allClassName = packeageDir.list();
			return allClassName;
		}
		return null;
	}

	private static Log logger = LogFactory.getLog(ClassUtil.class);

	/**
	 * 从包package中获取所有的Class
	 * 
	 * @param pack
	 * @return
	 */
	public static Set<String> getClasses(String packageName) {
		Set<String> classes = new LinkedHashSet<String>();
		boolean recursive = true;
		String packageDirName = packageName.replace('.', '/');
		Enumeration<URL> dirs;
		try {
			dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
			// 循环迭代下去
			while (dirs.hasMoreElements()) {
				URL url = dirs.nextElement();
				String protocol = url.getProtocol();
				if ("file".equals(protocol)) {
					// 获取包的物理路径
					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
					// 以文件的方式扫描整个包下的文件 并添加到集合中
					findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
				} else if ("jar".equals(protocol)) {
					logger.info("Found jar " + url.getPath() + " try load...");
					appendJar(url, packageDirName, packageName, recursive, classes);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return classes;
	}

	public static void appendJar(File file, String packageDirName, String packageName, boolean recursive,
			Set<String> classes) {

		try {
			JarFile jar = new JarFile(file);
			// 获取jar
			// 从此jar包 得到一个枚举类
			Enumeration<JarEntry> entries = jar.entries();
			// 同样的进行循环迭代
			while (entries.hasMoreElements()) {
				// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
				JarEntry entry = entries.nextElement();
				String name = entry.getName();
				// 如果是以/开头的
				if (name.charAt(0) == '/') {
					// 获取后面的字符串
					name = name.substring(1);
				}
				// 如果前半部分和定义的包名相同
				if (name.startsWith(packageDirName)) {
					int idx = name.lastIndexOf('/');
					// 如果以"/"结尾 是一个包
					if (idx != -1) {
						// 获取包名 把"/"替换成"."
						packageName = name.substring(0, idx).replace('/', '.');
					}
					// 如果可以迭代下去 并且是一个包
					if ((idx != -1) || recursive) {
						// 如果是一个.class文件 而且不是目录
						if (name.endsWith(".class") && !entry.isDirectory()) {
							// 去掉后面的".class" 获取真正的类名
							try{
								String className = name.substring(packageName.length() + 1, name.length() - 6);
								classes.add(getName(packageName, className));
							}catch(Throwable t){
								logger.fatal("Cannot add class " + name);
							}
						}
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void appendJar(URL url, String packageDirName, String packageName, boolean recursive,
			Set<String> classes) {
		// 如果是jar包文件
		// 定义一个JarFile
		JarFile jar;
		try {
			// 获取jar
			jar = ((JarURLConnection) url.openConnection()).getJarFile();
			// 从此jar包 得到一个枚举类
			Enumeration<JarEntry> entries = jar.entries();
			// 同样的进行循环迭代
			while (entries.hasMoreElements()) {
				// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
				JarEntry entry = entries.nextElement();
				String name = entry.getName();
				// 如果是以/开头的
				if (name.charAt(0) == '/') {
					// 获取后面的字符串
					name = name.substring(1);
				}
				// 如果前半部分和定义的包名相同
				if (name.startsWith(packageDirName)) {
					int idx = name.lastIndexOf('/');
					// 如果以"/"结尾 是一个包
					if (idx != -1) {
						// 获取包名 把"/"替换成"."
						packageName = name.substring(0, idx).replace('/', '.');
					}
					// 如果可以迭代下去 并且是一个包
					if ((idx != -1) || recursive) {
						// 如果是一个.class文件 而且不是目录
						if (name.endsWith(".class") && !entry.isDirectory()) {
							// 去掉后面的".class" 获取真正的类名
							String className = name.substring(packageName.length() + 1, name.length() - 6);
							classes.add(getName(packageName, className));
						}
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 以文件的形式来获取包下的所有Class
	 * 
	 * @param packageName
	 * @param packagePath
	 * @param recursive
	 * @param classes
	 */
	private static void findAndAddClassesInPackageByFile(String packageName, String packagePath,
			final boolean recursive, Set<String> classes) {
		// 获取此包的目录 建立一个File
		File dir = new File(packagePath);
		// 如果不存在或者 也不是目录就直接返回
		if (!dir.exists() || !dir.isDirectory()) {
			return;
		}
		// 如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = dir.listFiles(new FileFilter() {
			// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
			public boolean accept(File file) {
				return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
			}
		});
		// 循环所有文件
		for (File file : dirfiles) {
			// 如果是目录 则继续扫描
			if (file.isDirectory()) {
				findAndAddClassesInPackageByFile(getName(packageName, file.getName()), file.getAbsolutePath(),
						recursive, classes);
			} else {
				// 如果是java类文件 去掉后面的.class 只留下类名
				String className = file.getName().substring(0, file.getName().length() - 6);
				if (className.startsWith("package")) {
					continue;
				}
				classes.add(getName(packageName, className));
			}
		}
	}

	/**
	 * 获取包名称
	 * 
	 * @param packageName
	 * @param name
	 * @return
	 */
	private static String getName(String packageName, String name) {
		if (packageName.equals("")) {
			return name;
		}
		return packageName + "." + name;
	}

	/**
	 * 
	 * @param obj
	 * @param name
	 * @param value
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 */
	public static void setFieldValue(Object obj, String name, Object value) throws Exception {
		obj.getClass().getField(name).set(obj, value);
	}

	public static boolean isSimpleClass(Class<?> type) {
		if (type.isPrimitive()) {
			return true;
		} else if (type.getName().startsWith("java")) {
			return true;
		}
		return false;
	}

    /**
     * @return 当前对象是否为枚举
     */
    public static boolean isEnum(Class<?> type) {
        return type.isEnum();
    }
	public static boolean isSimple(Class<?> type) {
		return isStringLike(type) || isBoolean(type) || isChar(type) || isNumber(type) || isDateTimeLike(type)
				|| isEnum(type);
	}

	public static boolean isDateTimeLike(Class<?> type) {
		return Calendar.class.isAssignableFrom(type) || java.util.Date.class.isAssignableFrom(type)
				|| java.sql.Date.class.isAssignableFrom(type) || java.sql.Time.class.isAssignableFrom(type);
	}

	public static boolean isStringLike(Class<?> src) {
		return CharSequence.class.isAssignableFrom(src);
	}

	public static boolean isBoolean(Class<?> src) {
		return is(src, Boolean.class) || is(src, boolean.class);
	}

	public static boolean isChar(Class<?> src) {
		return is(src, Character.class) || is(src, char.class);
	}

	public static boolean isNumber(Class<?> src) {
		return Number.class.isAssignableFrom(src)
				|| src.isPrimitive() && !is(src, boolean.class) && !is(src, char.class);
	}

	public static boolean is(Class<?> src, Class<?> desc) {
		return null != desc && src == desc;
	}

	/**
	 * 获取field泛型类型
	 * 
	 * @param type
	 */
	public static Class<?> getFieldListGenType(Field field) {
		java.lang.reflect.Type type = field.getGenericType();
		if (type instanceof ParameterizedType) {
			ParameterizedType pType = (ParameterizedType) type;
			return (Class<?>) pType.getActualTypeArguments()[0];
		}
		return null;
	}

	public static Method[] getPublicMethods(Class<?> controllerClass) {

		Method[] methods = controllerClass.getMethods();
		List<Method> list = new ArrayList<Method>(methods.length);
		for (Method method : methods) {
			if ((method.getModifiers() & java.lang.reflect.Modifier.STATIC) > 0) {
				continue;
			}
			Class<?> cls = method.getDeclaringClass();
			if (cls != Object.class) {
				list.add(method);
			}
		}
		Method[] result = new Method[list.size()];
		list.toArray(result);
		return result;
	}

	public static boolean hasInterface(Class<?> clazz, Class<?> interfac) {
		Class<?>[] interfacesArray = clazz.getInterfaces();
		for (Class<?> i : interfacesArray) {
			if (i == interfac)
				return true;
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	public static <T> T newInstance(String className) throws Exception {
		Class<?> clazz = Class.forName(className);
		return (T) clazz.newInstance();
	}

	public static boolean isEqual(Class<?> srcClass, Class<?> destClass) {
		return srcClass == destClass || isWapClass(srcClass, destClass) || isWapClass(destClass, srcClass);
	}

	public static boolean isCollection(Class<? extends Object> class1) {
		// TODO Auto-generated method stub
		return false;
	}

}
